<script lang="ts" setup>
import type { SorterResult } from 'ant-design-vue/es/table/interface'
import type { TableColumnType, TablePaginationConfig } from 'ant-design-vue'
import { computed, onMounted, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import UploadSA from './components/UploadSAModal.vue'
import CveTable from './components/CveTable.vue'
import { orderMap } from './config'
import { api } from '@/api'
import type { Cve, Direction, Rpm } from '@/api/paths'
import PageWrapper from '@/components/PageWrapper.vue'

const isCveFixed = ref(false)
const filterMap: Record<string, string[]> = reactive({
  severity: [],
})

const { t } = useI18n()

const cveTableColumns = computed<TableColumnType[]>(() => [
  {
    key: 'cve_id',
    dataIndex: 'cve_id',
    title: 'CVE_ID',
    sorter: true,
  },
  {
    key: 'publish_time',
    dataIndex: 'publish_time',
    title: t('vul.cves.publishTime'),
    sorter: true,
  },
  {
    key: 'package',
    dataIndex: 'package',
    title: t('vul.cves.package'),
  },
  {
    key: 'severity',
    dataIndex: 'severity',
    title: t('vul.cves.severity'),
    filters: [
      {
        text: t('vul.severityStatus.critical'),
        value: 'Critical',
      },
      {
        text: t('vul.severityStatus.high'),
        value: 'High',
      },
      {
        text: t('vul.severityStatus.medium'),
        value: 'Medium',
      },
      {
        text: t('vul.severityStatus.low'),
        value: 'Low',
      },
      {
        text: t('vul.severityStatus.unknown'),
        value: 'Unknown',
      },
    ],
  },
  {
    key: 'cvss_score',
    dataIndex: 'cvss_score',
    title: t('vul.cves.cvssScore'),
    sorter: true,
  },
  {
    key: 'host_num',
    dataIndex: 'host_num',
    title: t('vul.cves.hosts'),
    sorter: true,
  },
])

const pagination = reactive<TablePaginationConfig>({
  total: 0,
  current: 1,
  pageSize: 10,
  showTotal: (total: number) => t('common.total', { count: total }),
  showSizeChanger: true,
  pageSizeOptions: ['10', '20', '30', '40'],
})

const tableState = reactive<{
  selectedRowKeys: string[]
  loading: boolean
}>({
  selectedRowKeys: [],
  loading: false,
})

const expandTableState = reactive<{
  selectedRowKeys: string[]
  getCheckboxProps: any
}>({
  selectedRowKeys: [],
  getCheckboxProps: () => {
    return {
      disabled: isCveFixed.value,
    }
  },
})

const cveTableData = ref<Cve[]>([])

/**
 * query cve list
 * @param searchKey filter by search key
 * @param sortKey sorted by sort key
 * @param sortDir sort type asc | desc
 */
async function queryCves(searchKey?: string, sortKey?: string, sortDir?: Direction) {
  tableState.loading = true
  tableState.selectedRowKeys = []
  expandTableState.selectedRowKeys = []
  const [, res] = await api.getCves({
    page: pagination.current,
    per_page: pagination.pageSize,
    filter: {
      affected: true,
      fixed: isCveFixed.value,
      search_key: searchKey || undefined,
      severity: filterMap.severity,
    },
    sort: sortKey,
    direction: sortDir,
  })
  if (res) {
    cveTableData.value = res.result
    pagination.total = res.total_count
  }
  tableState.loading = false
}

/**
 * Triggered when the table row select button is clicked, and check or deselect all the subtables
 */
function onSelect(record: Cve, selected: boolean) {
  if (selected) {
    tableState.selectedRowKeys.push(record.cve_id)
    if (!isCveFixed.value && record.rpms)
      selectAllExpandRow(record.rpms)
  }
  else {
    tableState.selectedRowKeys = tableState.selectedRowKeys.filter(
      item => item !== record.cve_id,
    )
    record.rpms && cancelAllExpandRow(record.rpms)
  }
}

function onSelectAll(selected: boolean, selectedRows: Cve[]) {
  if (selected) {
    tableState.selectedRowKeys = selectedRows.map((item) => {
      if (item.rpms && !isCveFixed.value)
        selectAllExpandRow(item.rpms)

      return item.cve_id
    })
  }
  else {
    tableState.selectedRowKeys = []
    expandTableState.selectedRowKeys = []
  }
}

/**
 * table changed, include pageation、filters、sorter
 */
function handleTableChange(page: TablePaginationConfig, filters: Record<string, string[] | null>, sorter: SorterResult<Cve>, searchKey?: string) {
  page.current && (pagination.current = page.current)
  page.pageSize && (pagination.pageSize = page.pageSize)
  filterMap.severity = filters.severity ?? []
  if (sorter.column) {
    const sortKey = sorter.columnKey
    const sortDir = sorter.order
    queryCves(searchKey, sortKey as string, orderMap[sortDir as Direction])
    return
  }
  queryCves(searchKey)
}

/**
 * switch cve is fixed status
 */
function handleRadioChange(checked: boolean) {
  isCveFixed.value = checked
  queryCves()
}

// #region ----------------------------------------< expand rpm table >----------------------------------------
const expandTableColumns = computed(() =>
  !isCveFixed.value
    ? [
        {
          key: 'installed_rpm',
          dataIndex: 'installed_rpm',
          title: t('vul.cves.unfixed.rpms.installedRpm'),
        },
        {
          key: 'available_rpm',
          dataIndex: 'available_rpm',
          title: t('vul.cves.unfixed.rpms.availableRpm'),
        },
        {
          key: 'support_way',
          dataIndex: 'support_way',
          title: t('vul.cves.supportWay'),
        },
        {
          key: 'host_num',
          dataIndex: 'host_num',
          title: t('vul.cves.hostNum'),
        },
      ]
    : [
        {
          key: 'installed_rpm',
          dataIndex: 'installed_rpm',
          title: t('vul.cves.fixed.rpms.installedRpm'),
        },
        {
          key: 'fixed_way',
          dataIndex: 'fixed_way',
          title: t('vul.cves.supportWay'),
        },
        {
          key: 'host_num',
          dataIndex: 'host_num',
          title: t('vul.cves.hostNum'),
        },
      ],
)

async function handleExpand(expanded: boolean, record: Cve) {
  if (!expanded)
    return
  await queryRpms(record.cve_id)
  if (tableState.selectedRowKeys.includes(record.cve_id)) {
    if (record.rpms && !isCveFixed.value)
      selectAllExpandRow(record.rpms)
  }
}

/**
 * Query rpm list through cve id and insert list into specified cve object
 * @param cveId
 */
async function queryRpms(cveId: string): Promise<void> {
  if (!cveTableData.value)
    return
  const cve = cveTableData.value.find(item => item.cve_id === cveId)
  if (!cve || cve.rpms)
    return
  if (!isCveFixed.value) {
    const [, res] = await api.getUnfixedRpm({ cve_id: cveId, host_ids: [] })
    if (res) {
      cve.rpms = res.map(item => ({
        ...item,
        cve_id: cve.cve_id,
      }))
    }
  }
  else {
    const [, res] = await api.getFixedRpm({ cve_id: cveId, host_ids: [] })
    if (res) {
      cve.rpms = res.map(item => ({
        ...item,
        cve_id: cve.cve_id,
      }))
    }
  }
}

/**
 * dealing with embedded list selection when slected one of options
 */
function onExpandSelect(record: Required<Rpm>, selected: boolean, fullList: Required<Rpm>[]) {
  const key = `${record.cve_id}-${record.installed_rpm}-${record.available_rpm}`
  selected
    ? expandTableState.selectedRowKeys.push(key)
    : (expandTableState.selectedRowKeys = expandTableState.selectedRowKeys.filter(
        item => item !== key,
      ))
  if (selected) {
    !tableState.selectedRowKeys.includes(record.cve_id)
    && tableState.selectedRowKeys.push(record.cve_id)
  }
  else {
    fullList.filter(item => item !== undefined).length === 0 && (tableState.selectedRowKeys = tableState.selectedRowKeys.filter(
      item => item !== record.cve_id,
    ))
  }
}

function onExpandSelectAll(selected: boolean, _selectedRows: Rpm[], changeRows: Required<Rpm>[]) {
  const cveId = changeRows[0].cve_id
  if (selected)
    !tableState.selectedRowKeys.includes(cveId) && tableState.selectedRowKeys.push(cveId)
  else
    tableState.selectedRowKeys = tableState.selectedRowKeys.filter(item => item !== cveId)

  changeRows.forEach((item) => {
    const key = `${item.cve_id}-${item.installed_rpm}-${item.available_rpm}`
    if (selected) {
      expandTableState.selectedRowKeys.push(key)
    }
    else {
      expandTableState.selectedRowKeys = expandTableState.selectedRowKeys.filter(
        item => item !== key,
      )
    }
  })
}

function selectAllExpandRow(rpms: Rpm[]) {
  rpms.forEach((item) => {
    const key = `${item.cve_id}-${item.installed_rpm}-${item.available_rpm}`
    expandTableState.selectedRowKeys.push(key)
  })
}

function cancelAllExpandRow(rpms: Rpm[]) {
  rpms.forEach((item) => {
    const key = `${item.cve_id}-${item.installed_rpm}-${item.available_rpm}`
    expandTableState.selectedRowKeys = expandTableState.selectedRowKeys.filter(
      item => item !== key,
    )
  })
}
// #endregion

onMounted(() => {
  queryCves()
})
</script>

<template>
  <PageWrapper>
    <a-card>
      <CveTable
        :is-fixed="isCveFixed"
        :table-columns="cveTableColumns"
        :expand-table-columns="expandTableColumns"
        :table-data="cveTableData"
        :pagination="pagination"
        :loading="tableState.loading"
        :row-selection="{
          selectedRowKeys: tableState.selectedRowKeys,
          onSelect,
          onSelectAll,
        }"
        :expand-row-selection="{
          selectedRowKeys: expandTableState.selectedRowKeys,
          onSelect: onExpandSelect,
          onSelectAll: onExpandSelectAll,
          getCheckboxProps: expandTableState.getCheckboxProps,
        }"
        @expand="handleExpand"
        @change="handleTableChange"
        @radio-change="handleRadioChange"
        @clear="
          tableState.selectedRowKeys = [];
          expandTableState.selectedRowKeys = [];
        "
      >
        <template #header-button>
          <UploadSA>
            <template #trigger>
              <a-button type="primary">
                {{ $t('vul.uploadSA') }}
              </a-button>
            </template>
          </UploadSA>
        </template>
      </CveTable>
    </a-card>
  </PageWrapper>
</template>
